#include "OptimizationEnvironment.h"
#include "BinaryChromosomeFactory.h"
#include "parameters.h"
#include "math.h"


OptimizationEnvironment::OptimizationEnvironment(int populationSize,
                                                 QVector< QPair<double, double> > _rangeVector,
                                                 int precision,
                                                 double (*_formulaPointer)(QVector<double>)) : AbstractEnvironment()
{
    if(_formulaPointer == NULL)
        qFatal("Null pointer - formula");

    formulaPointer = _formulaPointer;
    rangeVector = _rangeVector;

    //TODO test run formula

    int precisionBits;
    int totalChromosomeLength = 0;

    //count how many bits should represent one argument in chromosome
    for(int i=0;i<rangeVector.size();++i)
    {
        precisionBits = countPrecisionBits(rangeVector[i].first, rangeVector[i].second, precision);
        argumentBitsLegth.append(precisionBits);
        qDebug() << "precisionBits: " << precisionBits;
        totalChromosomeLength += precisionBits;
    }

    chromosomeFactory = new BinaryChromosomeFactory(totalChromosomeLength);
    createPopulation(populationSize);

}

QString OptimizationEnvironment::decodeChromosome(const Chromosome& chrom)
{
    QString retVal = "";
    QString chromosomeString = chrom.toString();
    QString chromosomeStringCut = "";
    QString chromosomeIntCut = "";
    QString arguments = "";

    bool ok;
    int leftBound = 0;
    double argument;

    for(int i=0;i<argumentBitsLegth.size();++i)
    {

        QString binaryArgument;
        binaryArgument = chromosomeString.mid(leftBound, argumentBitsLegth[i]);
        leftBound += argumentBitsLegth[i];

        chromosomeStringCut += binaryArgument;
        chromosomeIntCut += QString::number(binaryArgument.toInt(&ok,2));

        argument = decodeArgument(binaryArgument, rangeVector[i].first, rangeVector[i].second, argumentBitsLegth[i]);
        arguments += QString::number(argument, 'g', 20);

        if(i<argumentBitsLegth.size()-1)
        {
            chromosomeStringCut += "|";
            chromosomeIntCut += "|";
            arguments += "|";
        }
    }

    retVal += chromosomeString + "\n";
    retVal += chromosomeStringCut + "\n";
    retVal += chromosomeIntCut + "\n";
    retVal += arguments + "\n";
    retVal += "fitness: " + QString::number(chrom.getFitness(), 'g', 20) + "\n";

    return retVal;
}

void OptimizationEnvironment::fitnessFunction(Chromosome& chrom)
{
    double fitness = 0;
    int leftBound = 0;

    QString chromosomeString = chrom.toString();
    //qDebug() << chromosomeString;

    double argument;
    QVector<double> argumentVector;

    //cut chromosome and change to decimal
    for(int i=0;i<argumentBitsLegth.size();++i)
    {
        QString binaryArgument;
        binaryArgument = chromosomeString.mid(leftBound, argumentBitsLegth[i]);

        leftBound += argumentBitsLegth[i];

        argument = decodeArgument(binaryArgument, rangeVector[i].first, rangeVector[i].second, argumentBitsLegth[i]);

        argumentVector.append(argument);
    }

    fitness = formulaPointer(argumentVector);
    //qDebug() << fitness;

    chrom.setFitness(fitness);
}

double OptimizationEnvironment::decodeArgument(QString binaryRepresentation,
                                               double lowerBound,
                                               double upperBound,
                                               int precisionBits)
{
    bool ok;
    double argument;
    //TODO can be still to short for very long chromosomes
    qint64 integerRepresentation = binaryRepresentation.toInt(&ok,2);

    if(ok != true)
        qWarning() << "conversion error!";

    //count argument value
    argument = lowerBound +
               static_cast<double>(integerRepresentation)*(upperBound - lowerBound)/(pow(2.0, precisionBits) -1 );

    return argument;
}

int OptimizationEnvironment::countPrecisionBits(double lowerBound, double upperBound, int precision)
{
    double value;
    value = (upperBound - lowerBound) * pow(10.0, precision);

    //qDebug() << "value:" << value;

    int bits = 1;
    while(value > pow(2.0, bits))
        bits++;

    return bits;
}

